Supongamos que nos surge la necesidad de hacer un programa que crea 10 ficheros distintos con los nombres fichero0.txt, fichero1.txt, fichero2.txt y así hasta el fichero9.txt. El programa abre cada fichero y escribe una frase como "Este es el fichero X" donde la X es el número de cada uno. Es decir, el fichero5.txt contiene "Este es el fichero 5". Pongamos un nombre a este programa: le llamaremos Generador.
De hecho, antes de empezar Generador, haremos uno que está relacionado. Es este:
#include <iostream> using namespace std; int main() { for (int i = 0; i < 10; i++) { cout << "fichero" << i << ".txt" << endl; } }
La relación que hay entre ambos está en los nombres de fichero y el bucle. El programa Generador necesita los 10 nombres de fichero porque tiene que llamar al método open con esos nombres. Y el programa que acabamos de ver muestra esos 10 nombres por la pantalla.
Lo que haremos es arreglar este programa para acercarlo a Generador:
#include <iostream> #include <fstream> using namespace std; int main() { ofstream F; for (int i = 0; i < 10; i++) { cout << "fichero" << i << ".txt" << endl; F.open("fichero0.txt"); F << "Este es el fichero " << i << endl; F.close(); } }
Esta modificación nos acerca bastante a Generador, pero tiene un problema grande, y es que genera 10 veces un solo fichero: fichero0.txt. Como se ve, el nombre no cambia con i, que es lo que queremos.
Vamos a necesitar una nueva clase, llamada ostringstream (un "canal" de salida a strings), que está declarada en el fichero de cabecera sstream, por tanto hay que añadir un include:
#include <sstream>
A partir de ahí podemos crear objetos de las clases stringstream, istringstream i ostringstream. Si creamos un objeto ostringstream estamos creando un "buffer" (una porción de memoria) donde poder ir acumulando un string. Lo que es interesante es la manera de hacer la acumulacion: se utiliza como cout o un fichero de salida (un stream de salida). Por ejemplo, las instrucciones:
stringstream S; S << "fichero" << 2 << ".txt";
crean un objeto S, y añaden al "buffer" 3 elementos, que se acumulan de la misma manera que saldrian por pantalla. La clave está en que los elementos que añadimos pueden ser enteros o cualquier cosa que hasta ahora mostrábamos por pantalla.
De todas maneras, todo esto ocurre sin que por la pantalla salga nada ni vaya a parar a ningún fichero. El objeto S reside completamente en la memoria.
Una vez hemos acumulado todo el texto que queríamos añadir a S, podemos obtener el string resultante con el método str:
cout << S.str() << endl;
Con la nueva clase el programa Generador se modifica así:
#include <iostream> #include <fstream> #include <sstream> using namespace std; int main() { ofstream F; for (int i = 0; i < 10; i++) { ostringstream S; S << "fichero" << i << ".txt"; // hemos quitado 'endl' string nombre_fichero = S.str(); F.open(nombre_fichero.c_str()); F << "Este es el fichero " << i << endl; F.close(); } }
Cada vez que queremos "fabricar" el nombre del fichero a partir del entero i, creamos un objeto stringstream, añadimos los 3 ítems "fichero", número y ".txt", y obtenemos el string con S.str(). Ese string se asigna a nombre_fichero, con el que llamamos a open (invocando a c_str convenientemente).
Hay que observar que se ha quitado el endl del final del nombre del fichero, porque si no, el caracter de final de línea formaría parte del nombre del fichero.
En preparación